# -*- coding: utf-8 -*-
"""
@Time    : 2022/9/17 09:13
@Author  : huanghuang
"""
import datetime
from decimal import Decimal
from typing import List

from pydantic import BaseModel, Field, root_validator, validator


# 电脑网站支付请求体
class AliPayPageRequestModel(BaseModel):
    out_trade_no: str = Field(..., max_length=64, description="商户订单号", )
    total_amount: float = Field(..., ge=0.01, lt=100000000, description="订单总金额，单位:元")
    subject: str = Field(..., max_length=256, description="订单标题")
    product_code: str = Field("FAST_INSTANT_TRADE_PAY",
                              description="销售产品码,与支付宝签约的产品码名称,电脑支付场景仅支持FAST_INSTANT_TRADE_PAY")
    qr_pay_mode: str = Field("2", description="PC扫码支付的方式,2表示是跳转模式")
    qrcode_width: int = Field(100, description="商户自定义二维码宽度,在qr_pay_mode=4时生效")


# app支付请求体
class AliPayAppRequestModel(BaseModel):
    out_trade_no: str = Field(..., max_length=64, description="商户订单号", )
    total_amount: float = Field(..., ge=0.01, lt=100000000, description="订单总金额，单位:元")
    subject: str = Field(..., max_length=256, description="订单标题")
    passback_params: str = Field(None,
                                 max_length=512,
                                 description="公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。"
                                             "支付宝只会在同步返回（包括跳转回商户网站）和异步通知时将该参数原样返回。"
                                             "本参数必须进行UrlEncode之后才可以发送给支付宝。")


# 手机网站支付请求体
class AliPayWapRequestModel(BaseModel):
    out_trade_no: str = Field(..., max_length=64, description="商户订单号", )
    total_amount: float = Field(..., ge=0.01, lt=100000000, description="订单总金额，单位:元")
    subject: str = Field(..., max_length=256, description="订单标题")
    passback_params: str = Field(None,
                                 max_length=512,
                                 description="公用回传参数，如果请求时传递了该参数，则返回给商户时会回传该参数。"
                                             "支付宝只会在同步返回（包括跳转回商户网站）和异步通知时将该参数原样返回。"
                                             "本参数必须进行UrlEncode之后才可以发送给支付宝。")
    quit_url: str = Field(None, max_length=400, description="用户付款中途退出返回商户网站的地址")


class AliPayTradeQueryRequestModel(BaseModel):
    out_trade_no: str = Field(None, max_length=64, description="订单支付时传入的商户订单号,和支付宝交易号不能同时为空。"
                                                                 "trade_no,out_trade_no如果同时存在优先取trade_no")
    trade_no: str = Field(None, max_length=64, description="支付宝交易号，和商户订单号不能同时为空")

    @root_validator(pre=True)
    def pre_root_validate(cls, attrs: dict):
        if "out_trade_no" not in attrs and "trade_no" not in attrs:
            raise ValueError("缺失参数:out_trade_no或者trade_no")
        return attrs


class AliPayRefundRequestModel(BaseModel):
    out_trade_no: str = Field(None, max_length=64, description="订单支付时传入的商户订单号,和支付宝交易号不能同时为空。"
                                                                 "trade_no,out_trade_no如果同时存在优先取trade_no")
    trade_no: str = Field(None, max_length=64, description="支付宝交易号，和商户订单号不能同时为空")
    refund_amount: str = Field(..., description="退款金额")
    refund_reason: str = Field(None, max_length=256, description="退款原因说明")
    # 注：针对同一次退款请求，如果调用接口失败或异常了，重试时需要保证退款请求号不能变更，防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。
    out_request_no: str = Field(None, max_length=64, description="退款请求号。标识一次退款请求，需要保证在交易号下唯一，如需部分退款，则此参数必传。")

    @root_validator(pre=True)
    def pre_root_validate(cls, attrs: dict):
        if "out_trade_no" not in attrs and "trade_no" not in attrs:
            raise ValueError("缺失参数:out_trade_no或者trade_no")
        return attrs


class AliPayRefundQueryRequestModel(BaseModel):
    out_trade_no: str = Field(None, max_length=64, description="订单支付时传入的商户订单号,和支付宝交易号不能同时为空。"
                                                                 "trade_no,out_trade_no如果同时存在优先取trade_no")
    trade_no: str = Field(None, max_length=64, description="支付宝交易号，和商户订单号不能同时为空")

    out_request_no: str = Field(..., max_length=64, description="退款请求号。请求退款接口时，传入的退款请求号，如果在退款请求时未传入，则该值为创建交易时的商户订单号。")

    @root_validator(pre=True)
    def pre_root_validate(cls, attrs: dict):
        if "out_trade_no" not in attrs and "trade_no" not in attrs:
            raise ValueError("缺失参数:out_trade_no或者trade_no")
        return attrs


class AliPayTradeCloseRequestModel(BaseModel):
    out_trade_no: str = Field(None, max_length=64, description="订单支付时传入的商户订单号,和支付宝交易号不能同时为空。"
                                                                 "trade_no,out_trade_no如果同时存在优先取trade_no")
    trade_no: str = Field(None, max_length=64, description="支付宝交易号，和商户订单号不能同时为空")

    operator_id: str = Field(None, max_length=28, description="商家操作员编号 id，由商家自定义。")

    @root_validator(pre=True)
    def pre_root_validate(cls, attrs: dict):
        if "out_trade_no" not in attrs and "trade_no" not in attrs:
            raise ValueError("缺失参数:out_trade_no或者trade_no")
        return attrs


# ========== 国际版本 ============
class _AliPayNationPresentOrderAmount(BaseModel):
    currency: str = Field(..., max_length=3, description="币种")
    value: int = Field(..., ge=0, description="金额，单位是各个币种的 分")

class _AliPayNationPresentOrderGoodsUnionAmount(BaseModel):
    currency: str = Field(..., max_length=3, description="币种")
    value: int = Field(..., ge=1, description="金额，单位是各个币种的 分")

class _AliPayNationPresentOrderGoods(BaseModel):
    referenceGoodsId: str = Field(..., max_length=64, description="商品id")
    goodsName: str = Field(..., max_length=256, description="商品名")
    goodsUnitAmount: _AliPayNationPresentOrderGoodsUnionAmount
    goodsQuantity: int = Field(None, description="商品数量")


class _AliPayNationPresentOrderBuyerName(BaseModel):
    firstName: str = Field(..., max_length=32)
    middleName: str = Field(None, max_length=32)
    lastName: str = Field(..., max_length=32)
    fullName: str = Field(None, max_length=128)


class _AliPayNationPresentOrderBuyer(BaseModel):
    referenceBuyerId: str = Field(None, max_length=64, description="购买人id")
    buyerName: _AliPayNationPresentOrderBuyerName = Field(None, description="购买人姓名信息")
    buyerPhoneNo: str = Field(None, max_length=24, description="购买人手机号")
    buyerEmail: str = Field(None, max_length=256, description="购买人邮箱")


class _AliPayNationPresentOrderMerchantAddress(BaseModel):
    region: str = Field(..., max_length=2, description="国家编码")
    state: str = Field(None, max_length=8, description="州、国家或省的名称")
    city: str = Field(None, max_length=32, description="城市名字")
    address1: str = Field(None, max_length=256, description="地址")
    address2: str = Field(None, max_length=256, description="地址")
    zipCode: str = Field(None, max_length=32, description="邮政编码")


class _AliPayNationPresentOrderMerchantStore(BaseModel):
    referenceStoreId: str = Field(..., max_length=64, description="商户下面的商铺的唯一id")
    storeName: str = Field(..., max_length=256, description='门店的名字')
    storeMCC: str = Field(..., max_length=32, description="门店业务类别代码")
    storeDisplayName: str = Field(None, max_length=64, description="门店别名")
    storeAddress: _AliPayNationPresentOrderMerchantAddress = Field(None, description="门店地址")


class _AliPayNationPresentOrderMerchant(BaseModel):
    referenceMerchantId: str = Field(..., max_length=32, description="商户id")
    merchantMCC: str = Field(..., max_length=32, description="商户类别代码，即MCC 代码中列出的 4 位数字。支付宝+ MPP 必填。")
    merchantName: str = Field(..., max_length=256, description="商户名字")
    merchantDisplayName: str = Field(None, max_length=64, description="商户别名")
    merchantAddress: _AliPayNationPresentOrderMerchantAddress = Field(None, description="商户地址")
    merchantRegisterDate: datetime.datetime = Field(None, description="2019-11-27T12:01:01+08:00")
    store: _AliPayNationPresentOrderMerchantStore = Field(..., description="门店信息")

class _AliPayNationPresentOrder(BaseModel):
    orderAmount: _AliPayNationPresentOrderAmount
    referenceOrderId: str = Field(None, max_length=64, description="商户端识别订单的唯一ID，由直接向客户提供服务或商品的商户分配。该字段用于用户消费记录的展示和进一步的支付操作，如客户投诉和纠纷跟踪。")
    orderDescription: str = Field(..., max_length=256, description="订单摘要描述，用于用户消费记录展示或其他进一步操作")
    goods: List[_AliPayNationPresentOrderGoods] = Field(None, max_items=100, description="购买的商品")
    # shipping
    buyer: _AliPayNationPresentOrderBuyer = Field(None, description="订单购买人信息")
    merchant: _AliPayNationPresentOrderMerchant = Field(..., description="商户信息")
    # env
    # extendInfo


class _AliPayNationPresentPaymentMethod(BaseModel):
    paymentMethodType: str = Field("CONNECT_WALLET", max_length=32, description="有效值适用CONNECT_WALLET 于用户提供的模式付款")
    paymentMethodId: str = Field(..., max_length=128, description="付款方式的唯一 ID。该字段的值为支付码的码值。商家可以使用此字段来决定调用哪个支付服务来处理支付。")
    extendInfo: str = Field(None, max_length=2048, description="扩展信息")
    customerId: str = Field(None, max_length=32, description='购买人id')


class _AliPayNationPaymentFactor(BaseModel):
    inStorePaymentScenario: str = Field(..., description="根据这个区分支付方式")


# 支付宝国际版
class BaseAliPayNationRequestModel(BaseModel):
    productCode: str = Field("IN_STORE_PAYMENT", description="固定值")
    paymentRequestId: str = Field(..., description="当前支付请求的唯一id,需要做幂等")
    order: _AliPayNationPresentOrder = Field(..., description="订单信息")
    paymentAmount: _AliPayNationPresentOrderGoodsUnionAmount = Field(..., description="金额信息")
    paymentMethod: _AliPayNationPresentPaymentMethod = Field(..., description="支付方式信息")
    paymentExpiryTime: datetime.datetime = Field(None, description="2019-11-27T12:01:01+08:00")
    paymentNotifyUrl: str = Field(..., max_length=2048, description="支付回调地址")
    paymentFactor: _AliPayNationPaymentFactor = Field(...)


class AliPayNationUserPresentRequestModel(BaseAliPayNationRequestModel):

    @validator("paymentFactor", check_fields=False, always=True)
    def validate_inStorePaymentScenario(cls, value):
        if value.inStorePaymentScenario != "PaymentCode":
            raise ValueError("该值只能是:PaymentCode")
        return value


class AliPayNationOrderCodeRequestModel(BaseAliPayNationRequestModel):
    paymentRedirectUrl: str = Field(None, max_length=2048, description="跳转链接")
    @validator("paymentFactor", check_fields=False, always=True)
    def validate_inStorePaymentScenario(cls, value):
        if value.inStorePaymentScenario != "OrderCode":
            raise ValueError("该值只能是:OrderCode")
        return value


class AliPayNationEntryCodeRequestModel(BaseAliPayNationRequestModel):
    paymentRedirectUrl: str = Field(None, max_length=2048, description="跳转链接")
    @validator("paymentFactor", check_fields=False, always=True)
    def validate_inStorePaymentScenario(cls, value):
        if value.inStorePaymentScenario != "EntryCode":
            raise ValueError("该值只能是:EntryCode")
        return value


# 支付宝支付交易查询响应体 - success
class AliPayTradeQueryResponseModel(BaseModel):
    code: int
    msg: str
    trade_no: str = Field(..., description="支付宝交易订单号")
    out_trade_no: str = Field(..., description="商家订单号")
    buyer_user_id: str = Field(..., description="买家支付宝id")
    trade_status: str = Field(..., description="交易状态：WAIT_BUYER_PAY（交易创建，等待买家付款）、TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）、TRADE_SUCCESS（交易支付成功）、TRADE_FINISHED（交易结束，不可退款）")
    total_amount: Decimal = Field(..., description="交易传入的total_amount")
    buyer_pay_amount: Decimal = Field(None, description="买家实付金额")
    send_pay_date: datetime.datetime = Field(None, description="本次交易打款给卖家的时间")
    invoice_amount: Decimal = Field(None, description="可开发票金额")


